<!-- @format -->

<template>
  <view class="comp-tab">
    <!-- 标签栏 -->
    <view
      class="comp-tab__nav"
      :class="[
        `comp-tab__nav--${type}`,
        `comp-tab__nav--${size}`,
        `comp-tab__nav--${position}`,
        { 'comp-tab__nav--border': border },
        { 'comp-tab__nav--scrollable': scrollable },
      ]"
      :style="{ background }"
    >
      <scroll-view
        scroll-x
        :scroll-left="scrollLeft"
        scroll-with-animation
        :show-scrollbar="false"
        class="comp-tab__scroll"
        @scroll="handleScroll"
      >
        <view class="comp-tab__wrap">
          <view
            v-for="(tab, index) in tabList"
            :key="tab.name"
            class="comp-tab__item"
            :class="[
              { 'comp-tab__item--active': currentName === tab.name },
              { 'comp-tab__item--disabled': tab.disabled || disabled },
            ]"
            :style="getTabStyle(tab)"
            @click="onClickTab(tab, index)"
          >
            <!-- 图标 -->
            <view v-if="tab.icon" class="comp-tab__icon">
              <text class="comp-tab__icon-text">{{ tab.icon }}</text>
            </view>

            <!-- 标题 -->
            <text class="comp-tab__text">{{ tab.title }}</text>

            <!-- 徽标 -->
            <view v-if="tab.badge" class="comp-tab__badge">
              <text class="comp-tab__badge-text">{{ tab.badge }}</text>
            </view>
          </view>

          <!-- 下划线指示器 -->
          <view
            v-if="type === 'line'"
            class="comp-tab__line"
            :style="lineStyle"
          ></view>
        </view>
      </scroll-view>
    </view>

    <!-- 内容区域 -->
    <view
      class="comp-tab__content"
      :class="{ 'comp-tab__content--animated': animated }"
    >
      <view class="comp-tab__track" :style="trackStyle">
        <slot></slot>
      </view>
    </view>
  </view>
</template>

<script setup lang="ts">
  import { ref, computed, provide, watch, nextTick, onMounted } from 'vue';

  // 定义Tab项数据接口
  interface TabItem {
    /**
     * 标签名称
     */
    name: string | number;

    /**
     * 标签标题
     */
    title: string;

    /**
     * 是否禁用
     */
    disabled?: boolean;

    /**
     * 徽标内容
     */
    badge?: string | number;

    /**
     * 图标名称
     */
    icon?: string;
  }

  // 定义Tab尺寸类型
  type TabSize = 'small' | 'medium' | 'large';

  // 定义属性
  interface Props {
    /**
     * 当前激活的标签名称或索引值（支持v-model）
     */
    modelValue?: string | number;

    /**
     * 标签数据
     */
    tabs?: TabItem[];

    /**
     * 是否禁用所有标签
     */
    disabled?: boolean;

    /**
     * 标签栏背景色
     */
    background?: string;

    /**
     * 标签主题色
     */
    color?: string;

    /**
     * 标签栏样式类型
     */
    type?: 'line' | 'card' | 'text';

    /**
     * 是否显示底部边框
     */
    border?: boolean;

    /**
     * 是否可滚动
     */
    scrollable?: boolean;

    /**
     * 是否开启切换动画
     */
    animated?: boolean;

    /**
     * 是否懒渲染标签内容
     */
    lazyRender?: boolean;

    /**
     * 是否开启粘性布局
     */
    sticky?: boolean;

    /**
     * 粘性布局吸顶距离
     */
    offsetTop?: number;

    /**
     * 标签尺寸
     */
    size?: TabSize;

    /**
     * 标签栏位置
     */
    position?: 'top' | 'bottom';

    /**
     * 是否可滑动切换
     */
    swipeable?: boolean;
  }

  // 定义默认属性
  const props = withDefaults(defineProps<Props>(), {
    modelValue: '',
    tabs: () => [],
    disabled: false,
    background: '#ffffff',
    color: '#1989fa',
    type: 'line',
    border: true,
    scrollable: true,
    animated: false,
    lazyRender: true,
    sticky: false,
    offsetTop: 0,
    size: 'medium',
    position: 'top',
    swipeable: true,
  });

  // 定义事件
  const emit = defineEmits<{
    /**
     * 更新v-model绑定值
     */
    (e: 'update:modelValue', value: string | number): void;

    /**
     * 点击标签时触发
     */
    (e: 'click', name: string | number): void;

    /**
     * 当前激活的标签改变时触发
     */
    (e: 'change', name: string | number): void;

    /**
     * 滚动时触发
     */
    (e: 'scroll', params: { scrollLeft: number; isFixed: boolean }): void;

    /**
     * 粘性布局状态改变时触发
     */
    (e: 'sticky-change', isFixed: boolean): void;
  }>();

  // 标签列表
  const tabList = ref<TabItem[]>([...props.tabs]);

  // 当前激活的标签名
  const currentName = ref<string | number>(props.modelValue);

  // 滚动位置
  const scrollLeft = ref(0);

  // 是否固定
  const isFixed = ref(false);

  // 标签元素尺寸信息
  const tabRects = ref<any[]>([]);

  // 注册面板
  const registerPanel = (panel: TabItem) => {
    // 如果tabs属性中已有该面板，则不重复添加
    if (props.tabs.length > 0) return;

    const index = tabList.value.findIndex((item) => item.name === panel.name);
    if (index === -1) {
      tabList.value.push(panel);
    }
  };

  // 计算下划线样式
  const lineStyle = computed(() => {
    if (tabRects.value.length === 0) return {};

    const activeIndex = tabList.value.findIndex(
      (tab) => tab.name === currentName.value,
    );
    if (activeIndex === -1) return {};

    const activeRect = tabRects.value[activeIndex] || {};

    return {
      width: `${activeRect.width || 0}px`,
      transform: `translateX(${activeRect.left || 0}px)`,
      backgroundColor: props.color,
    };
  });

  // 计算内容区域轨道样式
  const trackStyle = computed(() => {
    if (!props.animated) return {};

    const activeIndex = tabList.value.findIndex(
      (tab) => tab.name === currentName.value,
    );
    if (activeIndex === -1) return {};

    return {
      transform: `translateX(-${activeIndex * 100}%)`,
      transitionDuration: '0.3s',
    };
  });

  // 获取标签样式
  const getTabStyle = (tab: TabItem) => {
    const active = currentName.value === tab.name;
    const style: Record<string, string> = {};

    if (active) {
      style.color = props.color;
    }

    return style;
  };

  // 点击标签
  const onClickTab = (tab: TabItem, index: number) => {
    if (tab.disabled || props.disabled) return;

    const name = tab.name;
    emit('click', name);

    if (currentName.value !== name) {
      currentName.value = name;
      emit('update:modelValue', name);
      emit('change', name);
    }

    scrollToTab(index);
  };

  // 滚动到指定标签
  const scrollToTab = (index: number) => {
    if (!props.scrollable || tabRects.value.length === 0) return;

    const rect = tabRects.value[index];
    if (!rect) return;

    scrollLeft.value = rect.left - (750 - rect.width) / 2;
  };

  // 处理滚动事件
  const handleScroll = (event: any) => {
    emit('scroll', {
      scrollLeft: event.detail.scrollLeft,
      isFixed: isFixed.value,
    });
  };

  // 处理粘性布局状态变化
  const handleStickyChange = (isSticky: boolean) => {
    isFixed.value = isSticky;
    emit('sticky-change', isSticky);
  };

  // 获取标签元素尺寸信息
  const updateTabRects = async () => {
    await nextTick();

    // 使用uni.createSelectorQuery获取元素尺寸
    const query = uni.createSelectorQuery();
    query
      .selectAll('.comp-tab__item')
      .boundingClientRect((rects) => {
        if (Array.isArray(rects) && rects.length > 0) {
          tabRects.value = rects;

          // 初始滚动到当前激活的标签
          const activeIndex = tabList.value.findIndex(
            (tab) => tab.name === currentName.value,
          );
          if (activeIndex !== -1) {
            scrollToTab(activeIndex);
          }
        }
      })
      .exec();
  };

  // 监听modelValue变化
  watch(
    () => props.modelValue,
    (val) => {
      if (val !== currentName.value) {
        currentName.value = val;
        const index = tabList.value.findIndex((tab) => tab.name === val);
        if (index !== -1) {
          scrollToTab(index);
        }
      }
    },
  );

  // 监听tabs变化
  watch(
    () => props.tabs,
    (val) => {
      tabList.value = [...val];
      updateTabRects();
    },
    { deep: true },
  );

  // 监听currentName变化
  watch(
    () => currentName.value,
    () => {
      updateTabRects();
    },
  );

  // 组件挂载后初始化
  onMounted(() => {
    // 如果没有设置初始值，默认选中第一个非禁用的标签
    if (!props.modelValue && tabList.value.length > 0) {
      const firstTab = tabList.value.find((tab) => !tab.disabled);
      if (firstTab) {
        currentName.value = firstTab.name;
        emit('update:modelValue', firstTab.name);
      }
    }

    updateTabRects();
  });

  // 提供给子组件的上下文
  provide('activeName', currentName);
  provide('animated', ref(props.animated));
  provide('lazyRender', ref(props.lazyRender));
  provide('registerPanel', registerPanel);

  // 暴露方法
  defineExpose({
    /**
     * 滚动到指定标签
     */
    scrollTo: (name: string | number) => {
      const index = tabList.value.findIndex((tab) => tab.name === name);
      if (index !== -1) {
        scrollToTab(index);
      }
    },

    /**
     * 获取当前激活的标签名称
     */
    getActiveName: () => currentName.value,

    /**
     * 设置当前激活的标签
     */
    setActiveName: (name: string | number) => {
      const tab = tabList.value.find((tab) => tab.name === name);
      if (tab && !tab.disabled && !props.disabled) {
        currentName.value = name;
        emit('update:modelValue', name);
        emit('change', name);

        const index = tabList.value.findIndex((tab) => tab.name === name);
        if (index !== -1) {
          scrollToTab(index);
        }
      }
    },
  });
</script>

<style lang="scss">
  .comp-tab {
    position: relative;
    width: 100%;

    &__nav {
      position: relative;
      display: flex;
      width: 100%;
      height: 44px;

      &--border::after {
        position: absolute;
        box-sizing: border-box;
        content: ' ';
        pointer-events: none;
        right: 0;
        left: 0;
        bottom: 0;
        transform: scaleY(0.5);
        border-bottom: 1px solid #ebedf0;
      }

      &--scrollable {
        overflow: hidden;
      }

      &--line {
        .comp-tab__item {
          padding: 0 16px;
        }
      }

      &--card {
        margin: 0 16px;
        height: 30px;
        background: #f7f8fa;
        border-radius: 2px;

        .comp-tab__item {
          color: #646566;
          border-right: 1px solid #f7f8fa;

          &--active {
            color: #fff;
            background-color: var(--tab-active-color, #1989fa);
          }
        }
      }

      &--text {
        .comp-tab__item {
          padding: 0 16px;

          &--active {
            font-weight: 500;
          }
        }
      }

      &--small {
        height: 36px;

        .comp-tab__item {
          font-size: 13px;
        }
      }

      &--medium {
        height: 44px;

        .comp-tab__item {
          font-size: 14px;
        }
      }

      &--large {
        height: 50px;

        .comp-tab__item {
          font-size: 16px;
        }
      }

      &--bottom {
        order: 1;
      }
    }

    &__scroll {
      width: 100%;
      height: 100%;
    }

    &__wrap {
      position: relative;
      display: flex;
      flex-wrap: nowrap;
      align-items: center;
      height: 100%;
    }

    &__item {
      position: relative;
      flex: 0 0 auto;
      display: flex;
      align-items: center;
      justify-content: center;
      box-sizing: border-box;
      color: #646566;
      cursor: pointer;
      height: 100%;

      &--active {
        color: var(--tab-active-color, #1989fa);
        font-weight: 500;
        border-bottom: 1px solid var(--tab-active-color, #1989fa);
      }

      &--disabled {
        color: #c8c9cc;
        cursor: not-allowed;
      }
    }

    &__text {
      display: inline-block;
    }

    &__icon {
      margin-right: 4px;
    }

    &__badge {
      margin-left: 4px;
      padding: 0 6px;
      color: #fff;
      font-size: 12px;
      height: 16px;
      line-height: 16px;
      border-radius: 8px;
      background-color: #ee0a24;

      &-text {
        display: inline-block;
        transform: scale(0.9);
      }
    }

    &__line {
      position: absolute;
      bottom: 0;
      height: 3px;
      border-radius: 3px;
      background-color: var(--tab-active-color, #1989fa);
      transition: all 0.3s;
    }

    &__content {
      overflow: hidden;

      &--animated {
        .comp-tab__track {
          position: relative;
          display: flex;
          width: 100%;
          transition-property: transform;

          .comp-tab-panel {
            flex: 0 0 100%;
            width: 100%;
          }
        }
      }
    }
  }
</style>
